Skip to content

达人探店

要点:

  • Set 的使用
  • ZSet 的使用

1.1 探店笔记

1.1.1 发布探店笔记

  1. 表结构

    • 这个功能涉及到两张表

      • tb_blog:探店笔记表,包含笔记中的标题、文字、图片等

        image-20260418191350109

      • tb_blog_comments:其他用户对探店笔记的评价

1.1.2 查看探店笔记

  1. 这里不仅要获取blog的基本数据,还要获取用户的一些基本数据

  2. 代码实现

    java
        @Override
        public Result getBlogById(Integer id) {
            Blog blog = blogMapper.selectById(id);
            if (blog == null) {
                return Result.fail("博客不存在!");
            }
            queryBlogUser(blog);
            return Result.ok(blog);
        }
    
        private void queryBlogUser(Blog blog) {
            Long userId = blog.getUserId();
            User user = userService.getById(userId);
            blog.setName(user.getNickName());
            blog.setIcon(user.getIcon());
        }

1.2 点赞

  1. 需求

    • 同一个用户只能点赞一次,再次点击则取消点赞
    • 如果当前用户已经点赞,则点赞按钮高亮显示
  2. 实现步骤

    • 给 Blog 类中添加一个 isLike 字段,标示是否被当前用户点赞
    • 修改点赞功能,利用 Redis 的 set 集合判断是否点赞过,未点赞过则点赞数+1,将用户放到set集合中 ,已点赞过则点赞数 -1,将用户从set集合中取出
    • 修改根据 id 查询 Blog 的业务,判断当前登录用户是否点赞过,赋值给 isLike 字段
    • 修改分页查询 Blog 业务,判断当前登录用户否点赞过,赋值给 isLike 字段
  3. 核心代码

    java
        /**
         *
         * 判断博客是否被当前用户点赞过
         *
         * @param blog
         * @return {@link Boolean }
         */
        private void isBlogLiked(Blog blog) {
            UserDTO user = UserHolder.getUser();
            if (user == null) {
                // 用户未登录,无需查询是否点赞
                return;
            }
            Long userId = user.getId();
            String key = BLOG_LIKED_KEY + blog.getId();
            Boolean isMember = stringRedisTemplate.opsForSet().isMember(key, userId.toString());
            // 点赞过设为 true 未点赞过设置为 false
            blog.setIsLike(BooleanUtil.isTrue(isMember));
        }
    
        @Override
        public void likeBlog(Long id) {
            // 实现点赞逻辑
            // 1. 修改点赞功能,利用 Redis 的 set 集合判断是否点赞过,未点赞过则点赞数+1,将用户放到set集合中 ,已点赞过则点赞数 -1,将用户从set集合中取出
            Long userId = UserHolder.getUser().getId();
    
            String key = BLOG_LIKED_KEY + id;
            Boolean liked = stringRedisTemplate.opsForSet().isMember(key, userId.toString());
    
            // 2. 如果未点赞过,则点赞数+1,将用户放到set集合中
            if (!Boolean.TRUE.equals(liked)) {
                // 更改数据库
                boolean success = update().setSql("liked = liked + 1").eq("id", id).update();
                // 更改 Redis
                if (success) {
                    stringRedisTemplate.opsForSet().add(key, userId.toString());
                }
            } else {
                // 3. 如果点赞过,则点赞数 -1,将用户从set集合中取出
                boolean success = update().setSql("liked = liked - 1").eq("id", id).update();
                if (success) {
                    stringRedisTemplate.opsForSet().remove(key, userId.toString());
                }
            }
        }

1.3 排行榜

  1. 需求

    • 点赞的用户应该在博客下方展示出来,按照权重排名
    • 展示top5
  2. 实现思路

    • 改用Zset,将用户放入集合时,将当前集合的个数作为 score ,将用户放入,最后展示的时候,升序排列,score低的排名高
  3. 代码实现

    java
    @Override
        public Result queryBlogLikesTop5(Long id) {
            String key = BLOG_LIKED_KEY + id;
            // 1. 查询 Redis 中 zset 集合,查询点赞数前5的用户 id
            // Spring Data Redis 默认返回的是一个 LinkedHashSet,保证了插入顺序
            Set<String> top5 = stringRedisTemplate.opsForZSet().range(key, 0, 4);
            if (top5 == null || top5.isEmpty()) {
                return Result.ok(Collections.EMPTY_LIST);
            }
            // 2. 根据用户 id 查询用户信息
            List<Long> ids = top5.stream().map(Long::valueOf).collect(Collectors.toList());
            List<UserDTO> dtoList = userService.query().in("id", ids).
                    last("ORDER BY FIELD(id," + StrUtil.join(",", ids) + ")")
                    .list().stream()
                    .map(user -> {
                        UserDTO userDTO = new UserDTO();
                        userDTO.setId(user.getId());
                        userDTO.setIcon(user.getIcon());
                        userDTO.setNickName(user.getNickName());
                        return userDTO;
                    }).collect(Collectors.toList());
            return Result.ok(dtoList);
        }